﻿using Newtonsoft.Json;
using Org.BouncyCastle.Asn1.Cmp;
using S7.Net;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.ElevatorManage;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Domain.LogDomain;
using Zocono.WCS.Domain.PlcBytesObject;
using Zocono.WCS.Domain.PlcConnectPoolDomain;
using Zocono.WCS.Domain.ProcessModuleDomain;
using Zocono.WCS.Infrastructure.ApllicationDto.Enums;
using Zocono.WCS.Infrastructure.PlcCommon;
using Zocono.WCS.WMSApiDomain.WebApi;

namespace Zocono.WCS.Domain.EquipmentProcessDomain.PlanarGeneralMessage
{
    /// <summary>
    /// 到达提升机前
    /// </summary>
    public class ArriveTsjService : IPlanarGeneralPlcActionProcessService
    {
        private readonly IRedisHelper _IRedisHelper;
        private readonly IElevatorJudgeInfoRepository _IElevatorJudgeInfoRepository;
        private readonly IPlcConnectionPool _PlcConnectionPool;
        private readonly IWorkTaskInfoRepository _WorkTaskInfoRepository;
        private readonly IPlcWriteDbInfoRepository _PlcWriteDbInfoRepository;
        private readonly IPlcReadDbInfoRepository _PlcReadeDbInfoRepository;
        private readonly IPlcConnectionInfoRepository _PlcConnectionInfoRepository;
        private readonly IElevatorInfoRepository _IElevatorInfoRepository;
        private readonly IPlcConnectionStateInfoRepository _IPlcConnectionStateInfoRepository;
        private readonly IRunLogInfoService _IRunLogInfoService;
        private readonly IElevatorMasterInfoRepository _IElevatorMasterInfoRepository;

        private readonly ILogUtil _logUtil;
        public ArriveTsjService(IRedisHelper iRedisHelper, IElevatorJudgeInfoRepository iElevatorJudgeInfoRepository
            , IElevatorMasterInfoRepository iElevatorMasterInfoRepository
            , IPlcConnectionPool plcConnectionPool
            , IWorkTaskInfoRepository workTaskInfoRepository
            , IPlcWriteDbInfoRepository plcWriteDbInfoRepository
            , IPlcConnectionInfoRepository plcConnectionInfoRepository
            , IElevatorInfoRepository elevatorInfoRepository
            , IPlcConnectionStateInfoRepository plcConnectionStateInfoRepository
            , IPlcReadDbInfoRepository plcReadDbInfoRepository
            , IRunLogInfoService iRunLogInfoService
            , ILogUtil logUtil)
        {
            _IRedisHelper = iRedisHelper;
            _IElevatorJudgeInfoRepository = iElevatorJudgeInfoRepository;
            _PlcConnectionPool = plcConnectionPool;
            _WorkTaskInfoRepository = workTaskInfoRepository;
            _PlcWriteDbInfoRepository = plcWriteDbInfoRepository;
            _PlcConnectionInfoRepository = plcConnectionInfoRepository;
            _IElevatorInfoRepository = elevatorInfoRepository;
            _IPlcConnectionStateInfoRepository = plcConnectionStateInfoRepository;
            _PlcReadeDbInfoRepository = plcReadDbInfoRepository;
            _IRunLogInfoService = iRunLogInfoService;
            _logUtil = logUtil;
            _IElevatorMasterInfoRepository = iElevatorMasterInfoRepository;
        }

        public async Task ProcessActionMessageAsync(BytesObjectBase plcActionMessage, BytesObjectBase wcsActionMessage, EquipmentDbObject planarEquipmentDbObject/*, BytesObjectBase plcStateMassge*/)
        {
            /*
            1.托盘到达提升机前，判断提升机是否空闲(电气分配到哪台提升机)
            2.根据托盘号、任务号获取执行中任务，获取路径是否存在该路径
            3.获取当前提升机的所有出口设备，查找任务目标位置需要经过的出口设备
            4.判断提升机出口设备是否被占位
             */

            var plcMessage = plcActionMessage as PlanarGeneralPlcActionBO;//Plc的动作报文
            var wcsMessage = wcsActionMessage as PlanarGeneralWcsActionBO;//Wcs的动作报文
            plcMessage.PalletBarcode = plcMessage.PalletBarcode.Trim();
            PlanarGeneralWriteWcsAction writeBo = null;//写入Plc的报文
            try
            {
                #region PLC连接池获取&判断plc连接是否空闲
                PlcConnectionS7 plcConn = _PlcConnectionPool.S7GetWritePlcConnection(plcMessage.PlcConnectionID);
                if (plcConn == null)
                {
                    _logUtil.Error($"到达提升机入口：【{plcMessage.EquipmentCode}】获取PLC连接失败");
                    return;
                }
                #endregion

                #region plc写入连接获取&写入报文构建
                RunLogInfo runLogInfo = new()
                {
                    ContainerBarcode = plcMessage.PalletBarcode,
                    EquipmentCode = plcMessage.EquipmentCode,
                    RunType = "跨楼层",

                    RunLog = "到达提升机入口"
                };

                if (plcMessage.AlrmCode != 0)
                {
                    runLogInfo.RunLog = $"报警,报警代码：{plcMessage.AlrmCode}";
                    _logUtil.Error($"到达提升机入口：【{plcMessage.EquipmentCode}】{runLogInfo.RunLog}");
                    return;
                }
                //写入plc的报文配置获取
                var objectType = typeof(PlanarGeneralWriteWcsAction).Name;
                var writeConfig = await _PlcWriteDbInfoRepository.GetFirstAsync(s => s.EquipmentCode == plcMessage.EquipmentCode && s.ObjectType == objectType);
                if (writeConfig == null)
                {
                    runLogInfo.RunLog = $"获取PLC写入配置失败";
                    _logUtil.Error($"到达提升机入口：【{plcMessage.EquipmentCode}】{runLogInfo.RunLog}");
                    return;
                }

                //构建写入报文
                writeBo = new PlanarGeneralWriteWcsAction(
                    writeConfig.PlcConnectionId,
                    writeConfig.ID,
                    writeConfig.DbCode,
                    writeConfig.DbStart,
                    objectType,
                    plcMessage.EquipmentCode.ToInt32(),
                    plcMessage.ActionType,
                    plcMessage.ToEquipmentCode,
                    taskCode: plcMessage.TaskCode,
                    palletBarcode: plcMessage.PalletBarcode,
                    plcMessage.PalletType,
                    alrmCode: plcMessage.AlrmCode);
                #endregion

                #region 获取任务信息及任务路径
                //3. 验证wcs任务，验证wcs当前子任务
                var exp = Expressionable.Create<WorkTaskInfo>();
                exp.AndIF(plcMessage.TaskCode > 0, it => it.TaskCode == plcMessage.TaskCode);
                exp.AndIF(true, it => it.PalletCode == plcMessage.PalletBarcode && it.TaskState == EnumTaskState.Executing);
                var task = await _WorkTaskInfoRepository.GetFirstAsync(exp.ToExpression());
                if (task == null)
                {
                    runLogInfo.RunLog = $"任务号({plcMessage.TaskCode})执行中的任务数据未查到";
                    _logUtil.Error($"到达提升机入口：【{plcMessage.EquipmentCode}】{runLogInfo.RunLog}");

                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 7;
                    var writeflag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                    _IRedisHelper.SetStringKey($"PLC-{plcMessage.EquipmentCode}", plcMessage.EquipmentCode + plcMessage.AlrmCode, TimeSpan.FromSeconds(10));
                    return;
                }

                var routeDetailExp = Expressionable.Create<RouteDetailInfo>();
                routeDetailExp.AndIF(true, l => l.ID == task.CurrentRounteDetailID);
                var routeDetail = await _WorkTaskInfoRepository.AsSugarClient()
                    .Queryable<RouteDetailInfo>()
                    .Where(routeDetailExp.ToExpression())
                    .Select(l => l)
                    .FirstAsync();

                if (routeDetail == null)
                {
                    runLogInfo.RunLog = $"巷道路径详情配置信息起始工位{task.WmsFromStation}目标工位{task.WmsToStation}获取设备路径失败";
                    _logUtil.Error($"到达提升机入口：【{plcMessage.EquipmentCode}】{runLogInfo.RunLog}");
                    return;
                }

                //托盘类型
                switch (task.PalletCode.Substring(1, 1))
                {
                    case "A":
                        writeBo.PalletType = 2;
                        break;
                    case "B":
                        writeBo.PalletType = 1;
                        break;
                    case "C":
                        writeBo.PalletType = 3;
                        break;
                }
                #endregion

                #region 获取所有提升机及其绑定的设备信息
                var readPlcDBConfigInfo = await _PlcReadeDbInfoRepository.GetListAsync();
                //获取所有提升机配置信息列表(出口/入口/可达到目标位)
                var elevtorMasterList = await _IElevatorMasterInfoRepository.GetListAsync();
                //根据入口设备号获取当前提升机配置信息
                ElevatorMasterInfo currentEleCrossInfo = elevtorMasterList.Find(it => it.CrossroadEquipmentCode == plcMessage.EquipmentCode);
                if (currentEleCrossInfo == null)
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"根据提升机入口【{plcMessage.EquipmentCode}】未找到提升机配置信息(ElevatorMasterInfo)";
                    _logUtil.Error(runLogInfo.RunLog);
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }
                //获取提升机读DB配置信息
                PlcReadDbInfo plcReadDbEle = readPlcDBConfigInfo.Find(l => l.EquipmentCode == currentEleCrossInfo.ElevatorCode);
                if (plcReadDbEle == null)
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"提升机【{currentEleCrossInfo.ElevatorCode}】入口【{plcMessage.EquipmentCode}】未找到读DB配置";
                    _logUtil.Error(runLogInfo.RunLog);
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }
                //获取提升机连接对象
                PlcConnectionS7 plcElevatorConn = _PlcConnectionPool.S7GetWritePlcConnection(plcReadDbEle.PlcConnectionID);
                if (plcElevatorConn == null)
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"十字口【{plcMessage.EquipmentCode}】对应提升机【{currentEleCrossInfo.ElevatorCode}】没有找到连接池对象！";
                    _logUtil.Error(runLogInfo.RunLog);
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }
                #endregion

                #region 判断提升机是否空闲
                ////读取数据
                //byte[] byteCurrentElevator = await plcElevatorConn.ReadDbAsync(plcReadDbEle.DbCode, plcReadDbEle.DbStart, plcReadDbEle.DbLength);
                //if (byteCurrentElevator == null)
                //{
                //    writeBo.ActionType = 99;
                //    writeBo.AlrmCode = 152;
                //    runLogInfo.RunLog = $"十字口【{plcMessage.EquipmentCode}】对应提升机【{currentEleCrossInfo.ElevatorCode}】读取提升机DB数据失败！";
                //    _logUtil.Error(runLogInfo.RunLog);
                //    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                //    return;
                //}
                ////序列化报文  当前提升机
                //ElevatorGeneralPlcActionBO plcActionBOCurrentElevator = new();
                //plcActionBOCurrentElevator.Serialize(plcReadDbEle.PlcConnectionID, plcReadDbEle.ID, plcReadDbEle.DbStart, plcReadDbEle.DbLength, plcReadDbEle.DbStart, plcReadDbEle.DbLength, "ElevatorGeneralPlcActionBO", plcReadDbEle.EquipmentCode, byteCurrentElevator);//主要是bytes转化成明文

                ////提升机忙  手动 报警状态 都不分配过去
                //if (plcActionBOCurrentElevator.Busy || !plcActionBOCurrentElevator.Auto || plcActionBOCurrentElevator.LiftAlrm)
                //{
                //    //当前交互十字口对应的提升机设备信息
                //    ElevatorInfo currentEleInfo = await _IElevatorInfoRepository.GetFirstAsync(it => it.ElevatorCode == currentEleCrossInfo.ElevatorCode);
                //    if (currentEleInfo == null)
                //    {
                //        writeBo.ActionType = 99;
                //        runLogInfo.RunLog = $"十字口【{plcMessage.EquipmentCode}】对应提升机【{currentEleCrossInfo.ElevatorCode}】没有找到配置数据！";
                //        _logUtil.Error(runLogInfo.RunLog);
                //        await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                //        return;
                //    }
                //    var canToNext = GetNextEleCanGo(elevtorMasterList, plcMessage.EquipmentCode, currentEleCrossInfo, currentEleInfo, task.CurrentToEquipment);
                //    if (canToNext)
                //    {
                //        writeBo.ActionType = 12;
                //        runLogInfo.RunLog = $"十字口【{plcMessage.EquipmentCode}】对应提升机【{currentEleCrossInfo.ElevatorCode}】状态忙或者手动或者报警！";
                //        _logUtil.Error(runLogInfo.RunLog);
                //        await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                //    }
                //    else
                //    {
                //        writeBo.ActionType = 99;
                //        writeBo.AlrmCode = 18;
                //        runLogInfo.RunLog = $"十字口【{plcMessage.EquipmentCode}】对应提升机【{currentEleCrossInfo.ElevatorCode}】已无路可走！";
                //        _logUtil.Error(runLogInfo.RunLog);
                //        await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                //    }
                //    return;
                //}
                #endregion

                #region 获取当前提升机的所有出口设备，查找任务目标位置需要经过的出口设备
                var tsjCurrentTaskDoEquipmentCode = string.Empty;//提升机当前任务的出口设备号
                var taskCurrentToEquipment = task.CurrentToEquipment.Trim();
                var taskCurrentToEquipmentFloor = taskCurrentToEquipment.Substring(0, 2);//提升机出口楼层
                var taskWmsToPosition = task.WmsToPosition.Trim();
                //获取当前提升机的所有出口设备
                var tsjElevatorDOCode = currentEleCrossInfo.ElevatorDOCodes;
                var tsjElevatorDOCodes = tsjElevatorDOCode.Split(',', StringSplitOptions.None);
                //获取当前提升机可去的所有设备
                var tsjCanToEquipment = currentEleCrossInfo.CanToEquipments;
                var tsjCanToEquipments = tsjCanToEquipment.Split(',', StringSplitOptions.None);

                //根据任务的目标设备号的前两位进行匹配提升机出口楼层设备号
                tsjCurrentTaskDoEquipmentCode = tsjElevatorDOCodes.Where(c => c.Substring(0, 2) == taskCurrentToEquipmentFloor).FirstOrDefault();
                if (string.IsNullOrEmpty(tsjCurrentTaskDoEquipmentCode))
                {
                    var currentCanToEquipment = tsjCanToEquipments.Where(c => c.Substring(0, 2) == taskCurrentToEquipmentFloor).FirstOrDefault();
                    tsjCurrentTaskDoEquipmentCode = tsjElevatorDOCodes.Where(c => c.Substring(0, 2) == currentCanToEquipment.Substring(0, 2)).FirstOrDefault();
                }
                if (string.IsNullOrEmpty(tsjCurrentTaskDoEquipmentCode))
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"提升机【{currentEleCrossInfo.ElevatorCode}】入口【{plcMessage.EquipmentCode}】的任务{task.TaskCode}|{task.TaskCode}|{taskCurrentToEquipment}没有找到对应出口设备号";
                    _logUtil.Error(runLogInfo.RunLog);
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }
                //获取提升机出口设备是否占位
                PlcReadDbInfo plcConfigDO = null;//出机台plc读配置
                if (plcMessage.ToEquipmentCode.ToString().Substring(0, 1) == "1")
                {
                    plcConfigDO = readPlcDBConfigInfo.Find(l => l.PlcName == $"DO{plcReadDbEle.EquipmentCode}" && l.EquipmentCode == tsjCurrentTaskDoEquipmentCode && l.EquipmentCode.Contains("130"));
                }
                else
                {
                    plcConfigDO = readPlcDBConfigInfo.Find(l => l.PlcName == $"DO{plcReadDbEle.EquipmentCode}" && l.EquipmentCode == tsjCurrentTaskDoEquipmentCode);
                }
                if (plcConfigDO == null)
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"未找到提升机{currentEleCrossInfo.ElevatorCode}出口{tsjCurrentTaskDoEquipmentCode}的读DB配置信息";
                    _logUtil.Error($"提升机{currentEleCrossInfo.ElevatorCode}入口{plcMessage.EquipmentCode}禁止接料，{runLogInfo.RunLog}");
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }

                //读取出库台DB
                PlcConnectionS7 plcDo = _PlcConnectionPool.S7GetWritePlcConnection(plcConfigDO.PlcConnectionID);
                byte[] bytesdo = await plcDo.ReadDbAsync(plcConfigDO.DbCode, plcConfigDO.DbStart, plcConfigDO.DbLength);
                if (bytesdo == null)
                {
                    runLogInfo.RunLog = $"读目标出库台DB失败查看是否离线";
                    _logUtil.Error($"到达入口：【{plcMessage.EquipmentCode}】{runLogInfo.RunLog}");
                    return;
                }
                PlanarGeneralPlcActionBO plcActionDO = new();
                plcActionDO.Serialize(plcConfigDO.PlcConnectionID, plcConfigDO.ID, plcConfigDO.DbStart, plcConfigDO.DbLength, plcConfigDO.DbStart, plcConfigDO.DbLength, "PlanarGeneralPlcActionBO", plcConfigDO.EquipmentCode, bytesdo);
                if (plcActionDO.PalletBarcode.Length > 0 || !string.IsNullOrWhiteSpace(plcActionDO.PalletBarcode))
                {
                    writeBo.ActionType = 99;
                    writeBo.AlrmCode = 151;
                    runLogInfo.RunLog = $"提升机出口{plcActionDO.EquipmentCode}有料";
                    _logUtil.Error($"提升机{currentEleCrossInfo.ElevatorCode}入口{plcMessage.EquipmentCode}禁止接料，{runLogInfo.RunLog}");
                    await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
                    return;
                }
                #endregion

                writeBo.TaskCode = task.TaskCode;
                writeBo.PalletBarcode = task.PalletCode;
                writeBo.ToEquipmentCode = task.CurrentToEquipment.ToInt32();
                writeBo.ActionType = 11;
                writeBo.AlrmCode = 0;
                await WriteDBToPlcEle(task, writeBo, plcMessage, plcConn, runLogInfo);
            }
            catch (Exception ex)
            {
                _logUtil.Error($"托盘{plcMessage.PalletBarcode}在{plcMessage.EquipmentCode}出现异常999：{ex.Message}");
            }
        }


        /// <summary>
        /// 写入PLC方法
        /// </summary>
        /// <param name="task"></param>
        /// <param name="writeBo"></param>
        /// <param name="plcMessage"></param>
        /// <param name="plcConn"></param>
        /// <param name="runLogInfo"></param>
        /// <returns></returns>
        public async Task WriteDBToPlcEle(WorkTaskInfo task, PlanarGeneralWriteWcsAction writeBo, PlanarGeneralPlcActionBO plcMessage, PlcConnectionS7 plcConn, RunLogInfo runLogInfo)
        {
            //写入正常报文
            writeBo.TaskCode = task.TaskCode;
            writeBo.PalletBarcode = task.PalletCode;
            writeBo.ToEquipmentCode = task.CurrentToEquipment.ToInt32();
            var writeBl = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());

            if (!writeBl)
            {
                runLogInfo.RunLog = $"十字口{plcMessage.EquipmentCode}申请入提升机写入正常报文失败，报文内容：{JsonConvert.SerializeObject(writeBo)}";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            }
            else
            {
                if (writeBo.ActionType == 11)
                {
                    runLogInfo.RunLog = $"十字口{plcMessage.EquipmentCode}申请入提升机允许进入写入正常报文成功，报文内容：{JsonConvert.SerializeObject(writeBo)}";
                    await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                }
                else if (writeBo.ActionType == 12)
                {
                    runLogInfo.RunLog = $"十字口{plcMessage.EquipmentCode}申请入提升机不允许进入原因：【{runLogInfo.RunLog}】;写入正常报文成功，报文内容：{JsonConvert.SerializeObject(writeBo)}";
                    await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                }
                else if (writeBo.ActionType == 99)
                {
                    runLogInfo.RunLog = $"十字口{plcMessage.EquipmentCode}申请入提升机进入报错原因：【{runLogInfo.RunLog}】;写入正常报文成功，报文内容：{JsonConvert.SerializeObject(writeBo)}";
                    await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                }
            }
        }
    }
}
